/*
 * Copyright (c) 2009, Servoy-stuff
 * 
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 * 
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
 * http://www.fsf.org/licensing/licenses/lgpl.txt
 */
package net.stuff.servoy.beans.calendar.swing;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.DateFormat;
import java.text.DateFormatSymbols;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.border.Border;
import javax.swing.border.EtchedBorder;
import javax.swing.text.JTextComponent;

import net.stuff.servoy.beans.calendar.DateChooserHelpers;
import net.stuff.servoy.beans.calendar.IDateChooserBeanComponent;

import com.servoy.j2db.IForm;
import com.servoy.j2db.Messages;
import com.servoy.j2db.dataprocessing.IModificationListener;
import com.servoy.j2db.dataprocessing.IRecord;
import com.servoy.j2db.dataprocessing.ModificationEvent;
import com.servoy.j2db.dataui.IServoyAwareBean;
import com.servoy.j2db.plugins.IClientPluginAccess;
import com.servoy.j2db.scripting.IScriptObject;
import com.servoy.j2db.scripting.JSEvent;
import com.servoy.j2db.ui.ILabel;
import com.servoy.j2db.util.ComponentFactoryHelper;
import com.servoy.j2db.util.Debug;
import com.servoy.j2db.util.PersistHelper;

import com.toedter.calendar.JDateChooser;

/**
 * SwingDateChooser is a "ServoyAware" implementation of the great 
 * JDateChooser bean from toedter.com (@see jcalendar-readme.txt for more)
 * 
 * @version 1.2 - 2010-03-01 - Servoy 5+ version
 * @version 1.0.2 - 2009-06-02 - Added AM/PM support and 12/24 hours
 * @version 1.0.1 - 2009-06-01 - Added Time controls
 * @author Servoy-stuff
 * http://www.servoy-stuff.net/
 */
public class SwingDateChooser extends JDateChooser 
implements IDateChooserBeanComponent, IServoyAwareBean, 
PropertyChangeListener, IModificationListener, 
IScriptObject, KeyEventDispatcher, ActionListener  {
	
	private static final long serialVersionUID = 1L;
	
	private IClientPluginAccess app;
	private IRecord currentRecord;
	private boolean validationMode = true;
	private String dataProviderID;
	private boolean editable = true;
	private boolean opaque = true;
	private boolean selectOnEnter;
	private String titleText;
	private String titleTextString;
	private String toolTip;
	private String format;
	private Date previousDate;
	private boolean valid = true;
	
	private int horizontalAlignment;
	
	private String onAction;
	private String onDataChange;
	private String onFocusGained;
	private String onFocusLost;
	
	private Map<?, ?> clientProperties;
	private List<ILabel> labels;
	private Border servoyBorder;
	private Color background;
	private Color foreground;
	
	private boolean readOnly;
	
	private TextJSpinField hours, minutes, seconds;
	private JButton ampm;
	private JPanel timeControls;
	
	private DateFormatSymbols dfs = new DateFormatSymbols();

	/**
	 * 
	 */
	public SwingDateChooser() {
		super(new JModifiedTextFieldEditor());
		Dimension d = new Dimension(140,20);
		setPreferredSize(d);
		setSize(140,20);
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.dataui.IServoyAwareBean#initialize(com.servoy.j2db.plugins.IClientPluginAccess)
	 */
	public void initialize(IClientPluginAccess app) {
		this.app = app;
		if (app.getApplicationType() == IClientPluginAccess.WEB_CLIENT) return;
		addPropertyChangeListener(this);
		dateEditor.addPropertyChangeListener(this);
		dateEditor.getUiComponent().addPropertyChangeListener(this);
		dateEditor.getUiComponent().addFocusListener(new FocusAdapter() {
			public void focusLost(FocusEvent e) {
				if (validationMode) {
					if (valid) {
						((JModifiedTextFieldEditor)dateEditor).focusLost(e);
						if (!e.getOppositeComponent().equals(getCalendarButton())) {
							fireFocusLostCallBack();
						}
					} else {
						if (!e.getOppositeComponent().equals(getCalendarButton()) 
								&& !e.getOppositeComponent().equals(popup)
								&& !e.getOppositeComponent().equals(jcalendar)
								&& !e.getOppositeComponent().equals(jcalendar.getDayChooser())) {
							dateEditor.getUiComponent().requestFocusInWindow();
						} else {
							((JModifiedTextFieldEditor)dateEditor).setDate(null, false);
							popup.requestFocusInWindow();
						}
					}
				}
			}
			public void focusGained(FocusEvent e) {
				if (validationMode) {
					if  (!e.getOppositeComponent().equals(getCalendarButton())) {
						if (valid) {
							fireFocusGainedCallBack();
						}
					}
				}
				if (isSelectOnEnter()) {
					((JModifiedTextFieldEditor)dateEditor).selectAll();
				}
			}
		});
		String tooltips = getToolTipText();
		if (tooltips != null) {
			String t = Messages.getStringIfPrefix(tooltips);
			((JModifiedTextFieldEditor)dateEditor).setToolTipText(t);
			dateEditor.getUiComponent().setToolTipText(t);
		}
		addFocusListener(new FocusAdapter() {
			public void focusGained(FocusEvent e) {
				if (!dateEditor.equals(e.getOppositeComponent()) &&
						!dateEditor.getUiComponent().equals(e.getOppositeComponent())
				) {
					dateEditor.getUiComponent().requestFocusInWindow();
				}
			}
		});
		((JModifiedTextFieldEditor)dateEditor).addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				((JModifiedTextFieldEditor)dateEditor).textCheck();
				setRecordValue();
				fireActionCallBack();
			}
		});
		
		KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
		kfm.addKeyEventDispatcher(this);
		
		if (Debug.tracing()) {
			kfm.addPropertyChangeListener(
			    new PropertyChangeListener() {
			        public void propertyChange(PropertyChangeEvent e) {
			            String properties = e.getPropertyName();
			            if (("focusOwner".equals(properties)) && (e.getNewValue() != null)) {
			                Component component = (Component)e.getNewValue();
			               	Debug.log(component.getName() + " ["+ component.getClass().getName()+"] has focus");
			            }
			        }
			    }
			);
		}
		
		setFocusTraversalPolicy(null);
		setFocusTraversalKeysEnabled(false);
		setFocusCycleRoot(false);
		
		calendarButton.setMnemonic(-1);
		
		setupTimeControls();
		addNotify();
		
	}
	
	/* (non-Javadoc)
	 * @see java.awt.KeyEventDispatcher#dispatchKeyEvent(java.awt.event.KeyEvent)
	 */
	public boolean dispatchKeyEvent(KeyEvent e) {
		if (e.getID() == KeyEvent.KEY_PRESSED) {
			Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
			if (dateEditor.equals(focusOwner) || dateEditor.getUiComponent().equals(focusOwner)) {
				boolean proceed = (validationMode) ? valid : true;
				if (proceed && e.getKeyCode() == KeyEvent.VK_TAB) {
					Debug.log("dispatchKeyEvent - "+e.paramString());
					if (e.isShiftDown()) {
						transferFocusBackward();
					} else {
						transferFocus();
					}
				}
			}
		}
		return false;
	}
	
	/* (non-Javadoc)
	 * @see com.servoy.j2db.dataui.IServoyAwareBean#setSelectedRecord(com.servoy.j2db.dataprocessing.IRecord)
	 */
	@SuppressWarnings("deprecation")
	public void setSelectedRecord(IRecord record) {
		if (DateChooserHelpers.isEmpty(dataProviderID)) return;
		if (app.getApplicationType() == IClientPluginAccess.WEB_CLIENT) return;
		this.currentRecord = record;
		if (currentRecord != null) {
			currentRecord.addModificationListner(this);
			Object obj = currentRecord.getValue(dataProviderID);
			setDateFromObject(obj);
			Date dt = (obj != null && obj instanceof Date) ? (Date)obj : null;
			previousDate = dt;
			this.setEnabled(!readOnly && editable);
			setValid();
		}
	}

	/**
	 * @return true if the dataprovider exists in the current foundset.
	 *
	private boolean isContainedInFoundset() {
		return DateChooserHelpers.isContainedInFoundset(currentRecord, dataProviderID);
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.dataprocessing.IDisplay#setValidationEnabled(boolean)
	 */
	public void setValidationEnabled(boolean validationMode) {
		this.validationMode = validationMode;
		((JModifiedTextFieldEditor)dateEditor).setValidationMode(validationMode);
	}
	
	/* (non-Javadoc)
	 * @see net.stuff.servoy.beans.calendar.IDateChooserBeanComponent#getValidationEnabled()
	 */
	public boolean getValidationEnabled() {
		return validationMode;
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.dataprocessing.IDisplay#stopUIEditing(boolean)
	 */
	public boolean stopUIEditing(boolean looseFocus) {
		boolean proceed = valid;
		if (!valid) {
			proceed = popup.isVisible() || dateSelected;
		}
		return (validationMode) ? proceed : true;
	}

	/* (non-Javadoc)
	 * @see net.stuff.servoy.beans.calendar.IDateChooserBeanComponent#getDataProviderID()
	 */
	public String getDataProviderID() {
		return this.dataProviderID;
	}

	/* (non-Javadoc)
	 * @see net.stuff.servoy.beans.calendar.IDateChooserBeanComponent#setDataProviderID(java.lang.String)
	 */
	public void setDataProviderID(String dataProvider) {
		this.dataProviderID = dataProvider;
	}
	
	/* (non-Javadoc)
	 * @see com.toedter.calendar.JDateChooser#propertyChange(java.beans.PropertyChangeEvent)
	 */
	@Override
	public void propertyChange(PropertyChangeEvent evt) {
		if (!timePropertyChange(evt)) {
			try {
				if (evt.getPropertyName().equals("day")) {
					if (popup.isVisible()) {
						dateSelected = true;
						popup.setVisible(false);
						((JModifiedTextFieldEditor)dateEditor).setDate(jcalendar.getCalendar().getTime(), false);
						if (valid) {
							dateEditor.getUiComponent().requestFocusInWindow();
						}
						Date d = getDate();
						Debug.log("propertyChange: "+previousDate + " => " + d);
						if (!DateChooserHelpers.equalsDate(previousDate, d, getDateFormatter())) {
							fireDataChangeCallBack(previousDate, d);
						}
						dateSelected = false;
						invalidate();
					}
				} else if (evt.getPropertyName().equals("date")) {
					if (validationMode) {
						((JModifiedTextFieldEditor)dateEditor).setDate((Date) evt.getNewValue(), false);
						Debug.log("propertyChange: "+previousDate + " => " + evt.getNewValue());
						if (!DateChooserHelpers.equalsDate(previousDate, evt.getNewValue(), getDateFormatter())) {
							fireDataChangeCallBack(previousDate, evt.getNewValue());
						}
					}
				} else {
					return;
				}
			} catch (Exception ex) {}
			if (isInitialized) {
				setRecordValue();
			}
		}
	}
	
	/**
	 * Set the current record value (java.util.Date or java.lang.String depending on the validationMode)
	 */
	private void setRecordValue() {
		if (currentRecord != null) {
			if (validationMode) {
				if (!DateChooserHelpers.equalsDate(currentRecord.getValue(dataProviderID), getDate(), getDateFormatter())) {
					if (currentRecord.startEditing()) {
						Debug.log("setRecordValue="+getTextValue()+" / Date="+getDate());
						Date dt = getDate();
						currentRecord.setValue(dataProviderID, dt);
						previousDate = dt;
					}
				}
				return;
			} else {
				if (!DateChooserHelpers.equalsDate(currentRecord.getValue(dataProviderID), getValue(), getDateFormatter())) {
					if (currentRecord.startEditing()) {
						Debug.log("setRecordValue= "+getValue());
						currentRecord.setValue(dataProviderID, getValue());
					}
				}
			}
		}
	}


	/* (non-Javadoc)
	 * @see com.servoy.j2db.dataprocessing.IModificationListener#valueChanged(com.servoy.j2db.dataprocessing.ModificationEvent)
	 */
	@SuppressWarnings("deprecation")
	public void valueChanged(ModificationEvent evt) {
		if (validationMode) {
			if (evt != null && evt.getRecord() != null) {
				if (evt.getRecord().equals(currentRecord)) {
					if (evt.getName().equals(dataProviderID)) {
						Object obj = evt.getValue();
						setDateFromObject(obj);
					}
				} else {
					evt.getRecord().removeModificationListner(this);
				}
			}
		}
	}
	
	/**
	 * Set the date (usually coming from a ModificationEvent)
	 * @param obj
	 */
	private void setDateFromObject(Object obj) {
		Date dt = (obj != null && obj instanceof Date) ? (Date)obj : null;
		((JModifiedTextFieldEditor)dateEditor).setDate(dt, false);
		invalidate();
	}

	/**
	 * @return Object (java.util.Date or java.lang.String)
	 */
	public Object getValue() {
		if (validationMode) {
			if (currentRecord != null) {
				return super.getDate();
			}
		}  else {
			return getTextValue();
		}
		return null;
	}

	/**
	 * Alias method
	 * @return the text of the inner field
	 */
	private String getTextValue() {
		return ((JTextComponent)dateEditor.getUiComponent()).getText();
	}
	
	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IComponent#getId()
	 */
	public String getId() {
		return (String)getClientProperty("Id");
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IComponent#setComponentEnabled(boolean)
	 */
	public void setComponentEnabled(boolean enable) {
		setEnabled(enable);
		
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IComponent#setComponentVisible(boolean)
	 */
	public void setComponentVisible(boolean visible) {
		setVisible(visible);
		
	}
	
	/* (non-Javadoc)
	 * @see javax.swing.JComponent#isOpaque()
	 */
	@Override
	public boolean isOpaque() {
		return opaque;
	}
	
	/* (non-Javadoc)
	 * @see javax.swing.JComponent#setOpaque(boolean)
	 */
	@Override
	public void setOpaque(boolean opaque) {
		this.opaque = opaque;
		super.setOpaque(opaque);
		if (dateEditor != null && dateEditor.getUiComponent() != null) {
			dateEditor.getUiComponent().setOpaque(opaque);
		}
	}
	
	/**
	 * @return the validColor
	 */
	public Color getValidColor() {
		return ((JModifiedTextFieldEditor)dateEditor).getValidColor();
	}

	/* (non-Javadoc)
	 * @see net.stuff.servoy.beans.calendar.IDateChooserBeanComponent#setValidColor(java.awt.Color)
	 */
	public void setValidColor(Color validColor) {
		((JModifiedTextFieldEditor)dateEditor).setValidColor(validColor);
	}

	/**
	 * @return the errorColor
	 */
	public Color getErrorColor() {
		return ((JModifiedTextFieldEditor)dateEditor).getErrorColor();
	}

	/* (non-Javadoc)
	 * @see net.stuff.servoy.beans.calendar.IDateChooserBeanComponent#setErrorColor(java.awt.Color)
	 */
	public void setErrorColor(Color errorColor) {
		((JModifiedTextFieldEditor)dateEditor).setErrorColor(errorColor);
	}

	/**
	 * @return the onAction
	 */
	public String getOnAction() {
		return this.onAction;
	}

	/* (non-Javadoc)
	 * @see net.stuff.servoy.beans.calendar.IDateChooserBeanComponent#setOnAction(java.lang.String)
	 */
	public void setOnAction(String onAction) {
		this.onAction = onAction;
	}

	/**
	 * @return the onDataChange
	 */
	public String getOnDataChange() {
		return this.onDataChange;
	}

	/* (non-Javadoc)
	 * @see net.stuff.servoy.beans.calendar.IDateChooserBeanComponent#setOnDataChange(java.lang.String)
	 */
	public void setOnDataChange(String onDataChange) {
		this.onDataChange = onDataChange;
	}

	/**
	 * @return the onFocusGained
	 */
	public String getOnFocusGained() {
		return this.onFocusGained;
	}

	/* (non-Javadoc)
	 * @see net.stuff.servoy.beans.calendar.IDateChooserBeanComponent#setOnFocusGained(java.lang.String)
	 */
	public void setOnFocusGained(String onFocusGained) {
		this.onFocusGained = onFocusGained;
	}

	/**
	 * @return the onFocusLost
	 */
	public String getOnFocusLost() {
		return this.onFocusLost;
	}

	/* (non-Javadoc)
	 * @see net.stuff.servoy.beans.calendar.IDateChooserBeanComponent#setOnFocusLost(java.lang.String)
	 */
	public void setOnFocusLost(String onFocusLost) {
		this.onFocusLost = onFocusLost;
	}

	/* (non-Javadoc)
	 * @see net.stuff.servoy.beans.calendar.IDateChooserComponent#getFormat()
	 */
	public String getFormat() {
		return format;
	}

	/* (non-Javadoc)
	 * @see net.stuff.servoy.beans.calendar.IDateChooserComponent#setFormat(java.lang.String)
	 */
	public void setFormat(String dateFormatString) {
		this.format = dateFormatString;
		setDateFormatString(Messages.getStringIfPrefix(dateFormatString));
	}

	/* (non-Javadoc)
	 * @see net.stuff.servoy.beans.calendar.IDateChooserComponent#getHorizontalAlignment()
	 */
	public int getHorizontalAlignment() {
		return horizontalAlignment;
	}

	/* (non-Javadoc)
	 * @see net.stuff.servoy.beans.calendar.IDateChooserComponent#setHorizontalAlignment(int)
	 */
	public void setHorizontalAlignment(int paramInt) {
		this.horizontalAlignment = paramInt;
		((JModifiedTextFieldEditor)dateEditor).setHorizontalAlignment(paramInt);
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.scripting.IScriptObject#getAllReturnedTypes()
	 */
	public Class[] getAllReturnedTypes() {
		return null;
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.scripting.IScriptObject#getParameterNames(java.lang.String)
	 */
	public String[] getParameterNames(String methodName) {
		if (TOOLTIP_SET_LOCATION.equals(methodName)) {
			return new String[] {"x", "y"};
		} else if (TOOLTIP_SET_SIZE.equals(methodName)) {
			return new String[] {"width", "height"};
		} else if (TOOLTIP_SET_BORDER.equals(methodName)) {
			return new String[] {"border"};
		} else if (TOOLTIP_GET_CLIENTPROP.equals(methodName)) {
			return new String[] {"key"};
		} else if (TOOLTIP_PUT_CLIENTPROP.equals(methodName)) {
			return new String[] {"key", "value"};
		} else if (TOOLTIP_SET_FONT.equals(methodName)) {
			return new String[] {"font"};
		} else if (TOOLTIP_REQUEST_FOCUS.equals(methodName)) {
			return new String[] {"[mustExecuteOnFocusGainedMethod]"};
		}
		return null;
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.scripting.IScriptObject#getSample(java.lang.String)
	 */
	public String getSample(String methodName) {
		StringBuffer buff = new StringBuffer();
		String tooltip = getToolTipFor(methodName);
		if (tooltip != null) {
			buff.append("//");
			buff.append(tooltip);
			buff.append("\n");
			if (TOOLTIP_GET_BGCOLOR.equals(methodName)) {
				buff.append("\tvar currentbg = %%elementName%%.bgcolor;\n");
				buff.append("\t%%elementName%%.bgcolor = currentbg;");
			} else if (TOOLTIP_GET_FGCOLOR.equals(methodName)) {
				buff.append("\tvar currentfg = %%elementName%%.fgcolor;\n");
				buff.append("\t%%elementName%%.fgcolor = currentfg;");
			} else if (TOOLTIP_IS_ENABLED.equals(methodName)) {
				buff.append("\tvar currentState = %%elementName%%.enabled;\n");
				buff.append("\t%%elementName%%.enabled = !currentState;");
			} else if (TOOLTIP_IS_VISIBLE.equals(methodName)) {
				buff.append("\tvar currentState = %%elementName%%.visible;\n");
				buff.append("\t%%elementName%%.visible = !currentState;");				
			} else if (TOOLTIP_GET_LOCATIONX.equals(methodName) || 
					TOOLTIP_GET_LOCATIONY.equals(methodName) ||
					TOOLTIP_SET_LOCATION.equals(methodName)) {
				buff.append("\tvar x = %%elementName%%.getLocationX();\n");
				buff.append("\tvar y = %%elementName%%.getLocationY();\n");
				buff.append("\t//set the new location\n");
				buff.append("\t%%elementName%%.setLocation(x+10,y+10);");
			} else if (TOOLTIP_GET_WIDTH.equals(methodName) ||
					TOOLTIP_GET_HEIGHT.equals(methodName) ||
					TOOLTIP_SET_SIZE.equals(methodName)) {
				buff.append("\tvar w = %%elementName%%.getWidth();\n");
				buff.append("\tvar h = %%elementName%%.getHeight();\n");
				buff.append("\t//set the new size\n");
				buff.append("\t%%elementName%%.setSize(w,h);");
			} else if (TOOLTIP_GET_NAME.equals(methodName)) {
				buff.append("\tvar name = %%elementName%%.getName();");
			} else if (TOOLTIP_SET_BORDER.equals(methodName)) {
				buff.append("\t%%elementName%%.setBorder('LineBorder,1,#ccffcc');");
			} else if (TOOLTIP_GET_ELEMENT_TYPE.equals(methodName)) {
				buff.append("\tvar type = %%elementName%%.getElementType();");
			} else if (TOOLTIP_GET_CLIENTPROP.equals(methodName)) {
				buff.append("\tvar property = %%elementName%%.getClientProperty('key');");
			} else if (TOOLTIP_PUT_CLIENTPROP.equals(methodName)) {
				buff.append("\t%%elementName%%.putClientProperty('key','value');\n");
				buff.append("\t//depending on platform some user interface client properties are available\n");
				buff.append("\t//example: %%elementName%%.putClientProperty('ToolTipText','some text')");
			} else if (TOOLTIP_TOOLTIP.equals(methodName)) {
				buff.append("\tvar txt = %%elementName%%.toolTipText;\n");
				buff.append("\t%%elementName%%.toolTipText = txt;");
			} else if (TOOLTIP_TRANSPARENCY.equals(methodName)) {
				buff.append("\tvar currentState = %%elementName%%.transparent;\n");
				buff.append("\t%%elementName%%.transparent = !currentState;");
			} else if (TOOLTIP_DATA_PROVIDER.equals(methodName)) {
				buff.append("\t%%elementName%%.getDataProviderID();");
			} else if (TOOLTIP_IS_EDITABLE.equals(methodName)) {
				buff.append("\tvar currentState = %%elementName%%.editable;\n");
				buff.append("\t%%elementName%%.editable = !currentState;");				
			} else if (TOOLTIP_SET_FONT.equals(methodName)) {
				buff.append("\t%%elementName%%.setFont('Tahoma,1,11');");
			} else if (TOOLTIP_GET_LABELFOR.equals(methodName)) {
				buff.append("\tvar array = %%elementName%%.getLabelForElementNames();\n");
				buff.append("\tfor (var i =0; i<array.length;i++)\n");
				buff.append("\t{\n");
				buff.append("\t\telements[array[i]].fgcolor = \"#ff00ff\";\n");
				buff.append("\t}");
			} else if (TOOLTIP_REQUEST_FOCUS.equals(methodName)) {
				buff.append("\t%%elementName%%.requestFocus();");
			}
			buff.append("\n\t");
			return buff.toString();
		}
		return null;
	}
	
	/**
	 * Utility method to return different tooltip depending on method name
	 * @param methodName
	 * @return
	 */
	private String getToolTipFor(String methodName) {
		if (TOOLTIP_GET_LOCATIONX.equals(methodName) || 
				TOOLTIP_GET_LOCATIONY.equals(methodName) ||
				TOOLTIP_SET_LOCATION.equals(methodName)) {
			return "Returns the X/Y location";
		} else if (TOOLTIP_GET_WIDTH.equals(methodName) ||
				TOOLTIP_GET_HEIGHT.equals(methodName) ||
				TOOLTIP_SET_SIZE.equals(methodName)) {
			return "Returns the width/height";
		} else if (TOOLTIP_SET_BORDER.equals(methodName)) {
			return "Set the border attributes, must be the same as borderType string from Element Properties";
		} else if (TOOLTIP_SET_FONT.equals(methodName)) {
			return "Set the font 'fontname,style,size',  style = (plain=0, bold=1, italic=2, bold-italic=3)";
		}
		return getToolTip(methodName);
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.scripting.IScriptObject#getToolTip(java.lang.String)
	 */
	public String getToolTip(String methodName) {
		if (TOOLTIP_GET_BGCOLOR.equals(methodName)) {
			return "Set / Get the background color"; 
		} else if (TOOLTIP_GET_FGCOLOR.equals(methodName)) {
			return "Set / Get the foreground color"; 
		} else if (TOOLTIP_IS_ENABLED.equals(methodName)) {
			return "Set / Get the enabled state (also called 'grayed')"; 
		} else if (TOOLTIP_IS_VISIBLE.equals(methodName)) {
			return "Set / Get the visiblity"; 			
		} else if (TOOLTIP_GET_LOCATIONX.equals(methodName)) {
			return "Returns the X location";
		} else if (TOOLTIP_GET_LOCATIONY.equals(methodName)) {
			return "Returns the Y location";
		} else if (TOOLTIP_SET_LOCATION.equals(methodName)) {
			return "Set the location";
		} else if (TOOLTIP_GET_WIDTH.equals(methodName)) {
			return "Returns the width";
		} else if (TOOLTIP_GET_HEIGHT.equals(methodName)) {
			return "Returns the height";
		} else if (TOOLTIP_SET_SIZE.equals(methodName)) {
			return "Set the size";
		} else if (TOOLTIP_GET_NAME.equals(methodName)) {
			return "Get the element name";
		} else if (TOOLTIP_SET_BORDER.equals(methodName)) {
			return "Set the border";
		} else if (TOOLTIP_GET_ELEMENT_TYPE.equals(methodName)) {
			return "Get the element type";
		} else if (TOOLTIP_GET_CLIENTPROP.equals(methodName)) {
			return "Get a client property by key.";
		} else if (TOOLTIP_PUT_CLIENTPROP.equals(methodName)) {
			return "Set a client property.";
		} else if (TOOLTIP_GET_FORMAT.equals(methodName)) {
			return "Set / Get the date format";
		} else if (TOOLTIP_IS_READONLY.equals(methodName)) {
			return "Set / Get the readOnly state";
		} else if (TOOLTIP_TOOLTIP.equals(methodName)) {
			return "Set / Get the toolTipText (when hovering over with mouse)";
		} else if (TOOLTIP_TRANSPARENCY.equals(methodName)) {
			return "Set / Get the transparency";
		} else if (TOOLTIP_DATA_PROVIDER.equals(methodName)) {
			return "Get the data provider this UI element (display) is showing";
		} else if (TOOLTIP_IS_EDITABLE.equals(methodName)) {
			return "Set / Get the editable state";
		} else if (TOOLTIP_SET_FONT.equals(methodName)) {
			return "Set the font";
		} else if (TOOLTIP_GET_LABELFOR.equals(methodName)) {
			return "Returns an Array of label element names that has this field filled in as the labelFor";
		} else if (TOOLTIP_REQUEST_FOCUS.equals(methodName)) {
			return "Request the focus in this %%elementName%% (focus is also a text cursor on text components)";
		}
		return null;
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.scripting.IScriptObject#isDeprecated(java.lang.String)
	 */
	public boolean isDeprecated(String methodName) {
		return false;
	}
	
	/**
	 * Lazy loading of the clientProperties Map
	 * @return the clientProperties Map
	 */
	private Map getClientProperties() {
		if (clientProperties == null) {
			clientProperties = new HashMap<Object, Object>();
		}
		return clientProperties;
	}
	
	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptBaseMethods#js_isVisible()
	 */
	public boolean js_isVisible() {
		return isVisible();
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptBaseMethods#js_setVisible(boolean)
	 */
	public void js_setVisible(boolean visible) {
		setVisible(visible);
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptBaseMethods#js_getBgcolor()
	 */
	public String js_getBgcolor() {
		return (getBackground() == null) ? null : PersistHelper.createColorString(getBackground());
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptBaseMethods#js_getClientProperty(java.lang.Object)
	 */
	public Object js_getClientProperty(Object paramObject) {
		return getClientProperties().get(paramObject);
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptBaseMethods#js_getElementType()
	 */
	public String js_getElementType() {
		return ELEMENT_TYPE;
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptBaseMethods#js_getFgcolor()
	 */
	public String js_getFgcolor() {
		return (getForeground() == null) ? null : PersistHelper.createColorString(getForeground());
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptBaseMethods#js_getHeight()
	 */
	public int js_getHeight() {
		return getHeight();
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptBaseMethods#js_getLocationX()
	 */
	public int js_getLocationX() {
		return getX();
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptBaseMethods#js_getLocationY()
	 */
	public int js_getLocationY() {
		return getY();
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptBaseMethods#js_getName()
	 */
	public String js_getName() {
		return getName();
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptBaseMethods#js_getWidth()
	 */
	public int js_getWidth() {
		return getWidth();
	}
	
	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptBaseMethods#js_putClientProperty(java.lang.Object, java.lang.Object)
	 */
	public void js_putClientProperty(Object paramObject1, Object paramObject2) {
		getClientProperties().put(paramObject1, paramObject2);
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptBaseMethods#js_setBgcolor(java.lang.String)
	 */
	public void js_setBgcolor(String paramString) {
		setBackground(PersistHelper.createColor(paramString));
	}


	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptBaseMethods#js_setBorder(java.lang.String)
	 */
	public void js_setBorder(String paramString) {
		setBorder(ComponentFactoryHelper.createBorder(paramString));
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptBaseMethods#js_setFgcolor(java.lang.String)
	 */
	public void js_setFgcolor(String paramString) {
		setForeground(PersistHelper.createColor(paramString));
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptBaseMethods#js_setLocation(int, int)
	 */
	public void js_setLocation(int paramInt1, int paramInt2) {
		setLocation(paramInt1, paramInt2);
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptBaseMethods#js_setSize(int, int)
	 */
	public void js_setSize(int paramInt1, int paramInt2) {
		setSize(paramInt1, paramInt2);
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptFormattedFieldMethods#js_getFormat()
	 */
	public String js_getFormat() {
		return getDateFormatString();
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptFormattedFieldMethods#js_setFormat(java.lang.String)
	 */
	public void js_setFormat(String dateFormatString) {
		setFormat(dateFormatString);
	}
		
	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptTransparentMethods#js_setToolTipText(java.lang.String)
	 */
	public void js_setToolTipText(String paramString) {
		setToolTipText(paramString);
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptTransparentMethods#js_getToolTipText()
	 */
	public String js_getToolTipText() {
		return getToolTipText();
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptTransparentMethods#js_setFont(java.lang.String)
	 */
	public void js_setFont(String paramString) {
		if (paramString != null) {
			setFont(PersistHelper.createFont(paramString));
		}
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptTransparentMethods#js_isTransparent()
	 */
	public boolean js_isTransparent() {
		return !isOpaque();
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptTransparentMethods#js_setTransparent(boolean)
	 */
	public void js_setTransparent(boolean transparent) {
		setOpaque(!transparent);
	}
	
	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptInputMethods#js_requestFocus(java.lang.Object[])
	 */
	public void js_requestFocus(Object[] paramArrayOfObject) {
		if (isDisplayable()) {
			dateEditor.getUiComponent().requestFocusInWindow();
		}
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptInputMethods#js_getDataProviderID()
	 */
	public String js_getDataProviderID() {
		return getDataProviderID();
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptInputMethods#js_getLabelForElementNames()
	 */
	public String[] js_getLabelForElementNames() {
		if (labels != null) {
			List temp = new ArrayList<String>(labels.size());
			for (Iterator iterator = labels.iterator(); iterator.hasNext();) {
				ILabel label = (ILabel) iterator.next();
				if (label.getName() != null && !"".equals(label.getName().trim())) {
					temp.add(label.getName());
				}
			}
			return (String[])temp.toArray(new String[temp.size()]);
		}
		return new String[0];
	}
	
	/**
	 * @return the Component DateFormat
	 */
	private DateFormat getDateFormatter() {
		return ((JModifiedTextFieldEditor)dateEditor).getDateFormatter();
	}
	
	/**
	 * Alias method 
	 * @see net.stuff.servoy.beans.calendar.swing.SwingDateChooser#fireActionCallBack(Object[] params)
	 * @param o1 (java.util.Date or java.lang.String)
	 * @param o2 (java.util.Date or java.lang.String)
	 */
	public void fireDataChangeCallBack(Object o1, Object o2) {
		String method = getOnDataChange();
		if (DateChooserHelpers.isNotEmpty(method)) {
			fireDataChangeCallBack(new Object[] {createJSEvent(method), o1, o2});
		}
		previousDate = (Date) o2;
	}
	
	/* (non-Javadoc)
	 * @see net.stuff.servoy.beans.calendar.IDateChooserBeanComponent#fireActionCallBack(java.lang.Object[])
	 */
	public void fireActionCallBack() {
		String method = getOnAction();
		firePropertyChange("date", previousDate, ((JModifiedTextFieldEditor)dateEditor).getDate());
		if (DateChooserHelpers.isNotEmpty(method)) {
			DateChooserHelpers.runFunctionByName(method, app, new Object[] {createJSEvent(method)});
		}
	}
	
	/* (non-Javadoc)
	 * @see net.stuff.servoy.beans.calendar.IDateChooserBeanComponent#fireDataChangeCallBack(java.lang.Object[])
	 */
	public void fireDataChangeCallBack(Object[] params) {
		DateChooserHelpers.runFunctionByName(getOnDataChange(), app, params);
	}
	
	/* (non-Javadoc)
	 * @see net.stuff.servoy.beans.calendar.IDateChooserBeanComponent#fireFocusLostCallBack(java.lang.Object[])
	 */
	public void fireFocusLostCallBack() {
		Date dt = ((JModifiedTextFieldEditor)dateEditor).getDate();
		Debug.log("fireFocusLostCallBack "+previousDate + " "+dt);
		firePropertyChange("date", previousDate, dt);
		String method = getOnFocusLost();
		if (DateChooserHelpers.isNotEmpty(method)) {
			DateChooserHelpers.runFunctionByName(method, app, new Object[] {createJSEvent(method)});
		}
	}
	
	/* (non-Javadoc)
	 * @see net.stuff.servoy.beans.calendar.IDateChooserBeanComponent#fireFocusGainedCallBack(java.lang.Object[])
	 */
	public void fireFocusGainedCallBack() {
		String method = getOnFocusGained();
		if (DateChooserHelpers.isNotEmpty(method)) {
			DateChooserHelpers.runFunctionByName(method, app, new Object[] {createJSEvent(method)});
		}
	}

	/* (non-Javadoc)
	 * @see net.stuff.servoy.beans.calendar.IDateChooserBeanComponent#getDateTextField()
	 */
	public Object getDateTextField() {
		return dateEditor;
	}

	/* (non-Javadoc)
	 * @see net.stuff.servoy.beans.calendar.IDateChooserBeanComponent#resetForeground()
	 */
	public void resetForeground() {}

	/* (non-Javadoc)
	 * @see net.stuff.servoy.beans.calendar.IDateChooserBeanComponent#setInvalid()
	 */
	public void setInvalid() {
		this.valid = false;
	}

	/* (non-Javadoc)
	 * @see net.stuff.servoy.beans.calendar.IDateChooserBeanComponent#setValid()
	 */
	public void setValid() {
		this.valid = true;
	}
	
	/* (non-Javadoc)
	 * @see net.stuff.servoy.beans.calendar.IDateChooserBeanComponent#isSelectOnEnter()
	 */
	public boolean isSelectOnEnter() {
		return this.selectOnEnter;
	}

	
	/* (non-Javadoc)
	 * @see net.stuff.servoy.beans.calendar.IDateChooserBeanComponent#setSelectOnEnter(boolean)
	 */
	public void setSelectOnEnter(boolean selectOnEnter) {
		this.selectOnEnter = selectOnEnter;
	}

	/* (non-Javadoc)
	 * @see net.stuff.servoy.beans.calendar.IDateChooserBeanComponent#getTitleText()
	 */
	public String getTitleText() {
		return titleText;
	}

	/* (non-Javadoc)
	 * @see net.stuff.servoy.beans.calendar.IDateChooserBeanComponent#setTitleText(java.lang.String)
	 */
	public void setTitleText(String titleText) {
		this.titleText = titleText;
	}
	
	/* (non-Javadoc)
	 * @see javax.swing.JComponent#setBorder(javax.swing.border.Border)
	 */
	@Override
	public void setBorder(Border border) {
		this.servoyBorder = border;
		super.setBorder(border);
	}
	
	/* (non-Javadoc)
	 * @see javax.swing.JComponent#getBorder()
	 */
	@Override
	public Border getBorder() {
		return servoyBorder;
	}
	
	/* (non-Javadoc)
	 * @see java.awt.Component#getForeground()
	 */
	@Override
	public Color getForeground() {
		return this.foreground;
	}

	/* (non-Javadoc)
	 * @see javax.swing.JComponent#setForeground(java.awt.Color)
	 */
	@Override
	public void setForeground(Color foreground) {
		this.foreground = foreground;
		super.setForeground(foreground);
	}

	
	/* (non-Javadoc)
	 * @see java.awt.Component#getBackground()
	 */
	@Override
	public Color getBackground() {
		return this.background;
	}

	/* (non-Javadoc)
	 * @see javax.swing.JComponent#setBackground(java.awt.Color)
	 */
	@Override
	public void setBackground(Color background) {
		this.background = background;
		super.setBackground(background);
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptBaseMethods#js_setEnabled(boolean)
	 */
	public void js_setEnabled(boolean paramBoolean) {
		setEnabled(paramBoolean);
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptBaseMethods#js_isEnabled()
	 */
	public boolean js_isEnabled() {
		return isEnabled();
	}
	
	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptInputMethods#js_isEditable()
	 */
	public boolean js_isEditable() {
		return isEditable();
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptInputMethods#js_setEditable(boolean)
	 */
	public void js_setEditable(boolean paramBoolean) {
		setEditable(paramBoolean);
	}

	/* (non-Javadoc)
	 * @see net.stuff.servoy.beans.calendar.IDateChooserBeanComponent#isEditable()
	 */
	public boolean isEditable() {
		return editable;
	}

	/* (non-Javadoc)
	 * @see net.stuff.servoy.beans.calendar.IDateChooserBeanComponent#setEditable(boolean)
	 */
	public void setEditable(boolean editable) {
		this.editable = editable;
		((JModifiedTextFieldEditor)dateEditor).setEditable(editable);
	}
	
	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptReadOnlyMethods#js_isReadOnly()
	 */
	public boolean js_isReadOnly() {
		return isReadOnly();
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptReadOnlyMethods#js_setReadOnly(boolean)
	 */
	public void js_setReadOnly(boolean readOnly) {
		setReadOnly(readOnly);
	}


	/* (non-Javadoc)
	 * @see com.servoy.j2db.dataprocessing.IDisplay#isReadOnly()
	 */
	public boolean isReadOnly() {
		return readOnly;
	}

	/**
	 * Set the readOnly state of this component and its children
	 * @param readOnly
	 */
	public void setReadOnly(boolean readOnly) {
		this.readOnly = readOnly;
		((JModifiedTextFieldEditor)dateEditor).setEditable(!readOnly);
		getCalendarButton().setEnabled(!readOnly);
	}

	/**
	 * @param servoyBorder the servoyBorder to set
	 */
	public void setServoyBorder(Border servoyBorder) {
		this.servoyBorder = servoyBorder;
		super.setBorder(servoyBorder);
	}
	
	/**
	 * Added time controls setup
	 */
	private void setupTimeControls() {
		timeControls = new JPanel(new GridLayout(1,4));
		timeControls.setBackground(Color.white);
		hours = new TextJSpinField(0,23);
		hours.setName("hours");
		minutes = new TextJSpinField(0,59);
		minutes.setName("minutes");
		seconds = new TextJSpinField(0,59);
		seconds.setName("seconds");
		ampm = new JButton();
		ampm.setFocusable(true);
		ampm.setBorderPainted(true);
		ampm.setBorder(new EtchedBorder(1));
		ampm.setContentAreaFilled(false);
		ampm.setName("ampm");
		ampm.setOpaque(false);
		
		WrapSpinnerNumberModel model24 = new WrapSpinnerNumberModel(0, 0, 23, 1);
		((JSpinner)hours.getSpinner()).setModel(model24);
		WrapSpinnerNumberModel model60 = new WrapSpinnerNumberModel(0, 0, 59, 1);
		((JSpinner)minutes.getSpinner()).setModel(model60);
		model60 = new WrapSpinnerNumberModel(0, 0, 59, 1);
		((JSpinner)seconds.getSpinner()).setModel(model60);
		
		String pattern = getDateFormatString();
		if (pattern != null) {
			boolean showAMPM = DateChooserHelpers.useAmPm(pattern);
			int min = DateChooserHelpers.hoursStartsWithOne(pattern) ? 1 : 0; 
			int max = (showAMPM) ? (11 + min) : (23 + min);
			hours.setMinimum(min);
			hours.setMaximum(max);
		}
		
		timeControls.add(ampm);
		timeControls.add(hours);
		timeControls.add(minutes);
		timeControls.add(seconds);
		
		jcalendar.add(timeControls, BorderLayout.SOUTH);

		hours.addPropertyChangeListener(this);
		minutes.addPropertyChangeListener(this);
		seconds.addPropertyChangeListener(this);
		
		hours.getTextField().addActionListener(this);
		minutes.getTextField().addActionListener(this);
		seconds.getTextField().addActionListener(this);
		ampm.addActionListener(this);
		getCalendarButton().addActionListener(this);
	}


	/* (non-Javadoc)
	 * @see com.toedter.calendar.JDateChooser#actionPerformed(java.awt.event.ActionEvent)
	 */
	public void actionPerformed(ActionEvent evt) {
		if (jcalendar.getCalendar() != null) {
			String pattern = getDateFormatString();
			Calendar c = (Calendar) jcalendar.getCalendar();
			boolean showAMPM = DateChooserHelpers.useAmPm(pattern);
			if (evt.getSource().equals(ampm)) {
				int am = ampm.getText().equals(dfs.getAmPmStrings()[Calendar.AM]) ? Calendar.PM : Calendar.AM;
				ampm.setText(dfs.getAmPmStrings()[am]);
				c.set(Calendar.AM_PM, am);
				ampm.invalidate();
				
			} else if (evt.getSource() instanceof JTextField) {
				super.actionPerformed(evt);
				if (showAMPM) {
					c.set(Calendar.HOUR, hours.getValue());
					ampm.setText(dfs.getAmPmStrings()[c.get(Calendar.AM_PM)]);
					ampm.invalidate();
				} else {
					c.set(Calendar.HOUR_OF_DAY, hours.getValue());
				}
				c.set(Calendar.MINUTE, minutes.getValue());
				c.set(Calendar.SECOND, seconds.getValue());
				
				jcalendar.setDate(c.getTime());
			} else {
				super.actionPerformed(evt);
				getDate(); // triggers the parsing
				c = (Calendar) jcalendar.getCalendar();
				
				boolean hoursEnabled = DateChooserHelpers.containHours(pattern);
				int min = DateChooserHelpers.hoursStartsWithOne(pattern) ? 1 : 0;
				hours.setEnabled(hoursEnabled);
				if (hoursEnabled) {
					hours.setUseTwoDigits(DateChooserHelpers.useTwoDigitHours(pattern));
					int max = (showAMPM) ? (11 + min) : (23 + min);
					hours.setMinimum(min);
					hours.setMaximum(max);
				}
				int hour = (showAMPM) ? c.get(Calendar.HOUR) : c.get(Calendar.HOUR_OF_DAY);
				if (min == 1 && hour == 0) {
					hour = hours.getMaximum();
				}
				hours.setValue((hoursEnabled)? hour : 0);
				if (showAMPM) {
					ampm.setText(dfs.getAmPmStrings()[c.get(Calendar.AM_PM)]);
				}
				timeControls.setOpaque(showAMPM);
				ampm.setVisible(showAMPM);
				
				boolean minutesEnabled = DateChooserHelpers.containMinutes(pattern);
				minutes.setEnabled(minutesEnabled);
				if (minutesEnabled) {
					minutes.setUseTwoDigits(DateChooserHelpers.useTwoDigitMinutes(pattern));
				}
				minutes.setValue((minutesEnabled)? c.get(Calendar.MINUTE) : 0);

				boolean secondsEnabled = DateChooserHelpers.containSeconds(pattern);
				seconds.setEnabled(secondsEnabled);
				if (secondsEnabled) {
					seconds.setUseTwoDigits(DateChooserHelpers.useTwoDigitSeconds(pattern));
				}
				seconds.setValue((secondsEnabled)? c.get(Calendar.SECOND) : 0);

				// shows the controls only if one is enabled at least:
				hours.getParent().setVisible(showAMPM || hoursEnabled || minutesEnabled || secondsEnabled);
				
			}
		}
	}
	
	/**
	 * Check if it is a time related propertyChangeEvent and act accordingly 
	 * by setting the controls value, or calendar value
	 * 
	 * @param evt
	 * @return true if it is a time related propertyChangeEvent, false otherwise
	 */
	private boolean timePropertyChange(PropertyChangeEvent evt) {
		if (jcalendar.getCalendar() != null) {
			String pattern = getDateFormatString();
			boolean showAMPM = DateChooserHelpers.useAmPm(pattern);
			if (evt.getPropertyName().equals("value")) {
				Calendar c = (Calendar) jcalendar.getCalendar();
				if (evt.getSource().getClass().equals(TextJSpinField.class)) {
					TextJSpinField source = (TextJSpinField)evt.getSource();
					if (source.getName().equals("hours")) {
						if (showAMPM) {
							int am = ampm.getText().equals(dfs.getAmPmStrings()[Calendar.AM]) ? Calendar.AM : Calendar.PM;
							c.set(Calendar.AM_PM, am);
							c.set(Calendar.HOUR, hours.getValue());
							ampm.setText(dfs.getAmPmStrings()[c.get(Calendar.AM_PM)]);
							invalidate();
						} else {
							c.set(Calendar.HOUR_OF_DAY, source.getValue());
						}
					} else if (source.getName().equals("minutes")) {
						c.set(Calendar.MINUTE, source.getValue());
					} else if (source.getName().equals("seconds")) {
						c.set(Calendar.SECOND, source.getValue());
					}
					jcalendar.getCalendar().setTime(c.getTime());
				} else {
					int min = DateChooserHelpers.hoursStartsWithOne(pattern) ? 1 : 0;
					int hour = (showAMPM) ? c.get(Calendar.HOUR) : c.get(Calendar.HOUR_OF_DAY);
					if (min == 1 && hour == 0) {
						hour = hours.getMaximum();
					}
					hours.setValue(hour);
					
					minutes.setValue(c.get(Calendar.MINUTE));
					seconds.setValue(c.get(Calendar.SECOND));
					if (showAMPM) {
						ampm.setText(dfs.getAmPmStrings()[c.get(Calendar.AM_PM)]);
					}
				}
				return true;
			}
		}
		return false;
	}

	/**
	 * @return the titleTextString
	 */
	public String getTitleTextString() {
		return this.titleTextString;
	}

	/**
	 * @param titleTextString the titleTextString to set
	 */
	public void setTitleTextString(String titleTextString) {
		this.titleTextString = Messages.getStringIfPrefix(titleTextString);
	}

	/**
	 * @return the toolTipText
	 */
	public String getToolTipText() {
		return this.toolTip;
	}

	/**
	 * @param toolTipText the toolTipText to set
	 */
	public void setToolTipText(String toolTipText) {
		this.toolTip = toolTipText;
		String t = Messages.getStringIfPrefix(toolTip);
		super.setToolTipText(t);
		((JModifiedTextFieldEditor)dateEditor).setToolTipText(t);
		dateEditor.getUiComponent().setToolTipText(t);
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptBaseMethods#js_getAbsoluteFormLocationY()
	 */
	public int js_getAbsoluteFormLocationY() {
		return 0;
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IScriptTitleTextMethods#js_getTitleText()
	 */
	public String js_getTitleText() {
		return titleText;
	}

	protected IForm getParentForm() {
		Container parent = getParent();
		while (true) {
			if (parent == null) {
				break;
			}
			if (parent instanceof IForm) {
				return (IForm)parent;
			}
			parent = parent.getParent();
		}
		return null;
	}
	
	protected JSEvent createJSEvent(final String method) {
		final JSEvent event = new JSEvent();
		event.setElementName(getName());
		if (app != null) {
			String frmName = null;
			if (DateChooserHelpers.isGlobal(method)) {
				final IForm frm = getParentForm();
				if (frm != null) {
					frmName = frm.getName();
				}
			} else {
				final int indexOfDot = method.indexOf('.');
				if (indexOfDot > -1) {
					frmName = method.substring(0, indexOfDot);
				}
			}
			if (frmName == null) {
				event.setFormName(app.getFormManager().getCurrentForm().getName());
			} else {
				event.setFormName(frmName);
			}
		}
		event.setSource(this);
		event.setType(JSEvent.NONE);
		event.setTimestamp(new Date());
		return event;
	}
}
